package com.alex.utils.html.parser;

import java.util.LinkedList;

import org.apache.commons.lang.StringEscapeUtils;

import com.alex.utils.html.HtmlUtils;
import com.alex.utils.html.element.ContainerTag;
import com.alex.utils.html.element.Tag;
import com.alex.utils.html.element.Text;

public class HtmlParser {

	public static ContainerTag parseHtml(String html) {
		ContainerTag root = null;
		LinkedList<ContainerTag> stack = new LinkedList<ContainerTag>();

		int startOfTag = 0, endOfTag = -1;
		while (true) {
			startOfTag = html.indexOf('<', endOfTag + 1);
			if (startOfTag < 0) {
				break;
			}

			if (endOfTag > 0 && endOfTag < startOfTag) {
				// Previous end of tag position less than start of current tag
				if (!stack.isEmpty()) {
					stack.getLast().addChild(new Text(StringEscapeUtils.unescapeHtml(html.substring(endOfTag + 1, startOfTag))));
				} else {
					// Ignore text without container tag
				}
			}

			endOfTag = html.indexOf('>', startOfTag + 1);
			if (endOfTag < 0) {
				break;
			}

			String tag = html.substring(startOfTag + 1, endOfTag).toLowerCase();
			if (tag.startsWith("!") || tag.endsWith("]")) {
				// Skip CDATAs, comments and DOCTYPEs
				continue;
			}

			if (tag.startsWith("/")) {
				String tagName = tag.substring(1);
				if (!stack.isEmpty() && stack.getLast().getName().equals(tagName)) {
					stack.removeLast();
				}
			} else {
				String tagName = null;
				int attrsPos = tag.indexOf(" ");
				if (attrsPos >= 0) {
					tagName = tag.substring(0, attrsPos);
				} else {
					tagName = tag;
				}

				if (tagName.endsWith("/")) {
					// Void tag
					if (!stack.isEmpty()) {
						stack.getLast().addChild(new Tag(tagName.substring(0, tagName.length() - 1)));
					}
					continue;
				}

				if (!HtmlUtils.isVoidTag(tagName)) {
					ContainerTag containerTag = new ContainerTag(tagName);
					if (!stack.isEmpty()) {
						stack.getLast().addChild(containerTag);
					}
					stack.addLast(containerTag);
					if (root == null) {
						root = containerTag;
					}
				} else {
					if (!stack.isEmpty()) {
						stack.getLast().addChild(new Tag(tagName));
					}
				}
			}
		}
		return root;
	}
}
